﻿// <copyright file="ResourceGenerator.cs" company="QUT">
//     Copyright (c) QUT. All rights reserved.
// </copyright>

using System;
using System.Collections.Generic;
using System.Data;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Resources;
using System.Text;

using Google.API;
using Google.API.Translate;

namespace LRT.Engine
{
    /// <summary>
    /// Localisation Resource Toolkit Engine Resource Generator
    /// </summary>
    public sealed class ResourceGenerator
    {
        /// <summary>
        /// Data table used for editing resources.
        /// </summary>
        private DataTable _resources;

        /// <summary>
        /// Source resource file path.
        /// </summary>
        private string _sourceFilePath;

        /// <summary>
        /// Source resource file culture (based on source resource file path).
        /// </summary>
        private string _sourceCulture;

        /// <summary>
        /// Target resource file culture.
        /// </summary>
        private string _targetCulture;

        /// <summary>
        /// Target resource file path.
        /// </summary>
        private string _targetFilePath;

        /// <summary>
        /// Database context for localisation resource toolkit database.
        /// </summary>
        private LocalisationResourceToolkitDataClassesDataContext _db;

        /// <summary>
        /// Initializes a new instance of the <see cref="ResourceGenerator"/> class.
        /// </summary>
        /// <param name="sourceFilePath">The source file path.</param>
        /// <param name="targetCulture">The target culture.</param>
        /// <param name="useTranslationMemory">if set to <c>true</c> [use translation memory].</param>
        /// <param name="useGoogleTranslate">if set to <c>true</c> [use google translate].</param>
        public ResourceGenerator(string sourceFilePath, string targetCulture, bool useTranslationMemory, bool useGoogleTranslate)
        {
            if (string.IsNullOrEmpty(sourceFilePath) || !sourceFilePath.EndsWith(".resx") || !File.Exists(sourceFilePath))
            {
                throw new ArgumentException("Invalid source resource file path.", "sourceResourceFilePath");
            }

            if (string.IsNullOrEmpty(targetCulture))
            {
                throw new ArgumentException("Invalid target culture", "targetCulture");
            }

            this._sourceFilePath = sourceFilePath;
            this._db = new LocalisationResourceToolkitDataClassesDataContext();
            this._sourceCulture = ResourceGenerator.GetSourceCulture(sourceFilePath);
            this._targetCulture = targetCulture;
            this.InitializeResources();
            this.LoadResources(sourceFilePath, useTranslationMemory, useGoogleTranslate);
        }

        /// <summary>
        /// Gets the resources.
        /// </summary>
        /// <value>The resources.</value>
        public DataTable Resources
        {
            get
            {
                return this._resources;
            }
        }

        /// <summary>
        /// Gets the target file path.
        /// </summary>
        /// <value>The target file path.</value>
        public string TargetFilePath
        {
            get
            {
                if (string.IsNullOrEmpty(this._targetFilePath))
                {
                    string directory = Path.GetDirectoryName(this._sourceFilePath);
                    string fileName = Path.GetFileName(this._sourceFilePath);
                    string[] parts = fileName.Split('.');
                    if (parts.Length == 2)
                    {
                        fileName = string.Format("{0}.{1}.{2}", parts[0], this._targetCulture, parts[1]);
                    }
                    else
                    {
                        fileName = string.Format("{0}.{1}.{2}", parts[0], this._targetCulture, parts[2]);
                    }

                    this._targetFilePath = Path.Combine(directory, fileName);
                }

                return this._targetFilePath;
            }
        }

        /// <summary>
        /// Gets the source culture.
        /// </summary>
        /// <param name="resourceFilePath">The resource file path.</param>
        /// <returns>The source culture.</returns>
        public static string GetSourceCulture(string resourceFilePath)
        {
            string[] parts = Path.GetFileName(resourceFilePath).Split('.');
            if (parts.Length != 3)
            {
                return string.Empty;
            }
            else
            {
                return parts[1];
            }
        }

        /// <summary>
        /// Updates the translation memory and creates and saves the target resource file.
        /// </summary>
        public void Save()
        {
            this.UpdateTranslationMemory();
            this.CreateTargetResourceFile();
        }

        /// <summary>
        /// Gets the translation memory.
        /// </summary>
        /// <param name="name">The string name.</param>
        /// <param name="sourceCulture">The source culture.</param>
        /// <param name="targetCulture">The target culture.</param>
        /// <param name="sourceValue">The source value.</param>
        /// <returns>The translation memory, if found.</returns>
        private TranslationMemory GetTranslationMemory(string name, string sourceCulture, string targetCulture, string sourceValue)
        {
            return (from tm in this._db.TranslationMemories
                    where tm.Name == name &&
                          tm.SourceCulture == sourceCulture &&
                          tm.TargetCulture == targetCulture &&
                          tm.SourceValue == sourceValue
                    select tm).FirstOrDefault();
        }

        /// <summary>
        /// Initializes the resources data table.
        /// </summary>
        private void InitializeResources()
        {
            this._resources = new DataTable();
            this._resources.Columns.Add("Name", typeof(string));
            this._resources.Columns.Add("SourceValue", typeof(string));
            this._resources.Columns.Add("TargetValue", typeof(string));
            this._resources.Columns.Add("TargetValueSource", typeof(int));
        }

        /// <summary>
        /// Loads the resources from the source resource file.
        /// </summary>
        /// <param name="filePath">The file path.</param>
        /// <param name="useTranslationMemory">if set to <c>true</c> [use translation memory].</param>
        /// <param name="useGoogleTranslate">if set to <c>true</c> [use google translate].</param>
        private void LoadResources(string filePath, bool useTranslationMemory, bool useGoogleTranslate)
        {
            this._resources.Rows.Clear();
            var reader = new ResXResourceReader(filePath);
            var en = reader.GetEnumerator();
            while (en.MoveNext())
            {
                if (en.Value.GetType() != typeof(string))
                {
                    continue;
                }

                string name = en.Key.ToString();
                string value = en.Value.ToString();
                var row = this._resources.NewRow();
                row[0] = name;
                row[1] = value;

                // Retrieve target value
                string targetValue = string.Empty;
                if (useTranslationMemory)
                {
                    targetValue = this.GetValueFromTranslationMemory(name, value);
                    row[3] = (int)TargetValueSource.TranslationMemory;
                }

                if (string.IsNullOrEmpty(targetValue) && useGoogleTranslate)
                {
                    targetValue = this.GetValueFromGoogleTranslate(this._sourceCulture, this._targetCulture, value);
                    row[3] = (int)TargetValueSource.GoogleTranslate;
                }

                if (string.IsNullOrEmpty(targetValue))
                {
                    targetValue = "No Match";
                    row[3] = (int)TargetValueSource.None;
                }

                row[2] = targetValue;
                this._resources.Rows.Add(row);
            }
        }

        /// <summary>
        /// Gets the target value from translation memory.
        /// </summary>
        /// <param name="name">The string name.</param>
        /// <param name="value">The source value.</param>
        /// <returns>The target value, if found.</returns>
        private string GetValueFromTranslationMemory(string name, string value)
        {
            var tm = this.GetTranslationMemory(name, this._sourceCulture, this._targetCulture, value);
            if (tm == null)
            {
                return null;
            }
            else
            {
                return tm.TargetValue;
            }
        }

        /// <summary>
        /// Gets the value from google translate.
        /// </summary>
        /// <param name="sourceCulture">The source culture.</param>
        /// <param name="targetCulture">The target culture.</param>
        /// <param name="sourceValue">The source value.</param>
        /// <returns>The translated value, if found.</returns>
        private string GetValueFromGoogleTranslate(string sourceCulture, string targetCulture, string sourceValue)
        {
            var sc = new CultureInfo(sourceCulture);
            var tc = new CultureInfo(targetCulture);
            TranslateClient client = new TranslateClient("http://www.qut.edu.au");
            try
            {
                return client.Translate(sourceValue, sc.TwoLetterISOLanguageName, tc.TwoLetterISOLanguageName);
            }
            catch (GoogleAPIException)
            {
                // Ignore error, internet connectivity may not be available.
                return null;
            }
        }

        /// <summary>
        /// Updates the translation memory.
        /// </summary>
        private void UpdateTranslationMemory()
        {
            foreach (DataRow row in this._resources.Rows)
            {
                string name = row[0].ToString();
                string sourceValue = row[1].ToString();
                string targetValue = row[2].ToString();
                if (string.IsNullOrEmpty(targetValue) || targetValue.Equals("No Match"))
                {
                    continue;
                }

                TranslationMemory tm = this.GetTranslationMemory(name, this._sourceCulture, this._targetCulture, sourceValue);
                if (tm == null)
                {
                    tm = new TranslationMemory();
                    tm.Name = name;
                    tm.SourceCulture = this._sourceCulture;
                    tm.TargetCulture = this._targetCulture;
                    tm.SourceValue = sourceValue;
                    tm.TargetValue = targetValue;
                    this._db.TranslationMemories.InsertOnSubmit(tm);
                }
                else
                {
                    tm.TargetValue = targetValue;
                }

                this._db.SubmitChanges();
            }
        }

        /// <summary>
        /// Creates the target resource file.
        /// </summary>
        private void CreateTargetResourceFile()
        {
            string filePath = this.TargetFilePath;
            if (File.Exists(filePath))
            {
                File.Delete(filePath);
            }

            ResXResourceWriter writer = new ResXResourceWriter(filePath);
            foreach (DataRow row in this._resources.Rows)
            {
                string value = row[2].ToString();
                if (string.IsNullOrEmpty(value) || value.Equals("No Match"))
                {
                    continue;
                }

                writer.AddResource(row[0].ToString(), row[2].ToString());
            }

            writer.Close();
        }
    }
}
